package com.xinqi.common.redis.topic;

import com.xinqi.common.redis.topic.annotation.Topic;
import com.xinqi.common.redis.topic.annotation.TopicRegister;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RTopic;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.InvocationTargetException;

/**
 * @author linkai
 * @date 2023/7/6
 */
@Slf4j
@RequiredArgsConstructor
public class TopicBasePostProcessor implements BeanPostProcessor {
    private final TopicHelp topicHelp;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean.getClass().isAnnotationPresent(TopicRegister.class)) {
            // 添加主题监听
            ReflectionUtils.doWithMethods(bean.getClass(),
                method -> {
                    Topic topic = method.getAnnotation(Topic.class);
                    RTopic rTopic = topicHelp.getTopic(topic.value());
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    Class<?> msgType = topic.type();

                    int typeIdx = -1;
                    int channelIdx = -1;
                    for (int i = 0; i < parameterTypes.length; i++) {
                        Class<?> parameterType = parameterTypes[i];
                        if (parameterType == msgType && typeIdx == -1) {
                            typeIdx = i;
                            continue;
                        }
                        if (parameterType == CharSequence.class && channelIdx == -1) {
                            channelIdx = i;
                        }
                    }
                    int[] topicParamIdx = new int[2];
                    topicParamIdx[0] = typeIdx;
                    topicParamIdx[1] = channelIdx;
                    rTopic.addListener(msgType, (channel, msg) -> {
                        Object[] args = new Object[parameterTypes.length];
                        try {
                            for (int i = 0; i < parameterTypes.length; i++) {
                                // 保存
                                if (topicParamIdx[0] == i) {
                                    args[i] = msg;
                                } else if (topicParamIdx[1] == i) {
                                    args[i] = channel;
                                } else {
                                    args[i] = null;
                                }
                            }
                            method.invoke(bean, args);
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            throw new RuntimeException(e);
                        }
                    });
                },
                method -> method.isAnnotationPresent(Topic.class)
            );
        }
        return bean;
    }
}
